package com.cutefool.zero.code.admin.dbms.support;

import com.cutefool.core.exec.BizException;
import com.cutefool.core.kits.consts.Status;
import com.cutefool.core.kits.util.Lists;
import com.cutefool.core.res.ResponseCode;
import com.cutefool.rds.enums.RdsDbType;
import com.cutefool.rds.meta.RdsMetaOperations;
import com.cutefool.rds.meta.libs.ColumnLibs;
import com.cutefool.rds.meta.libs.TableLibs;
import com.cutefool.zero.code.admin.dbms.dto.DbDTO;
import com.cutefool.zero.code.core.domain.Db;
import com.zaxxer.hikari.HikariDataSource;

import java.util.List;
import java.util.stream.Collectors;

public class DbmsMetas {

    public static void test(DbDTO db) {
        if (!Status.isTrue(db.getMultiplex())) {
            RdsDbType rdsDbType = rdsDbType(db.getType());
            try (HikariDataSource dataSource = DbmsMetas.dataSource(db, rdsDbType)) {
                RdsMetaOperations rdsMetaOperations = rdsDbType.getRdsMetaOperationsFunction().apply(dataSource);
                rdsMetaOperations.test();
            } catch (Exception e) {
                throw new BizException(e, ResponseCode.API_ERROR.getCode(), "DB连接错误: " + db.getHost() + ":" + db.getPort());
            }
        }
    }

    public static List<String> tables(Db db) {
        RdsDbType rdsDbType = rdsDbType(db.getType());
        try (HikariDataSource dataSource = DbmsMetas.dataSource(db, rdsDbType)) {
            RdsMetaOperations rdsMetaOperations = rdsMetaOperations(dataSource, rdsDbType);
            List<TableLibs> tables = rdsMetaOperations.tablesBySchema(db.getName());
            return Lists.empty(tables).stream().filter(TableLibs::isTable).map(TableLibs::getName).collect(Collectors.toList());
        } catch (Exception e) {
            throw new BizException(e, ResponseCode.API_ERROR.getCode(), "DB连接错误: " + db.getHost() + ":" + db.getPort());
        }
    }

    public static List<ColumnLibs> columns(Db db, String table) {
        RdsDbType rdsDbType = rdsDbType(db.getType());
        try (HikariDataSource dataSource = DbmsMetas.dataSource(db, rdsDbType)) {
            RdsMetaOperations rdsMetaOperations = rdsMetaOperations(dataSource, rdsDbType);
            return rdsMetaOperations.columnBy(db.getName(), table);
        } catch (Exception e) {
            throw new BizException(e, ResponseCode.API_ERROR.getCode(), "DB连接错误: " + db.getHost() + ":" + db.getPort());
        }
    }

    public static RdsMetaOperations rdsMetaOperations(HikariDataSource dataSource, RdsDbType rdsDbType) {
        return rdsDbType.getRdsMetaOperationsFunction().apply(dataSource);
    }

    public static RdsDbType rdsDbType(String type) {
        return RdsDbType.byName(type).orElseThrow(() -> new BizException(ResponseCode.API_ERROR.getCode(), "不支持数据库类型[" + type + "]"));
    }

    public static HikariDataSource dataSource(DbDTO db) {
        return dataSource(db, rdsDbType(db.getType()));
    }

    public static HikariDataSource dataSource(DbDTO db, RdsDbType rdsDbType) {
        return dataSource(db.getUsername(), db.getPassword(), db.getHost(), db.getPort(), db.getName(), rdsDbType);
    }

    public static HikariDataSource dataSource(Db db) {
        return dataSource(db, rdsDbType(db.getType()));
    }

    public static HikariDataSource dataSource(Db db, RdsDbType rdsDbType) {
        return dataSource(db.getUsername(), db.getPassword(), db.getHost(), db.getPort(), db.getName(), rdsDbType);
    }

    public static HikariDataSource dataSource(String username, String password, String host, String port, String name, RdsDbType rdsDbType) {
        HikariDataSource hikariDataSource = new HikariDataSource();
        hikariDataSource.setUsername(username);
        hikariDataSource.setPassword(password);
        hikariDataSource.setDriverClassName(rdsDbType.getDriver());
        String jdbc = rdsDbType.getJdbc();
        String replace = jdbc.replace("{host}", host).replace("{port}", port).replace("{database}", name);
        hikariDataSource.setJdbcUrl(replace);
        hikariDataSource.setMaximumPoolSize(1);
        hikariDataSource.setMaximumPoolSize(1);
        return hikariDataSource;
    }
}
